local ffi = require("ffi");
ffi.cdef[[
    typedef enum 
    {
	FCEUI_STOPAVI, FCEUI_QUICKSAVE, FCEUI_QUICKLOAD, FCEUI_SAVESTATE, FCEUI_LOADSTATE,
	FCEUI_NEXTSAVESTATE,FCEUI_PREVIOUSSAVESTATE,FCEUI_VIEWSLOTS,
	FCEUI_STOPMOVIE, FCEUI_RECORDMOVIE, FCEUI_PLAYMOVIE,
	FCEUI_OPENGAME, FCEUI_CLOSEGAME,
	FCEUI_TASEDITOR,
	FCEUI_RESET, FCEUI_POWER, FCEUI_PLAYFROMBEGINNING, FCEUI_EJECT_DISK, FCEUI_SWITCH_DISK, FCEUI_INSERT_COIN
    }EFCEUI;

    typedef enum 
{
	SI_UNSET		= -1,
	SI_NONE			= 0,
	SI_GAMEPAD		= 1,
	SI_ZAPPER		= 2,
	SI_POWERPADA	= 3,
	SI_POWERPADB	= 4,
	SI_ARKANOID		= 5,
	SI_MOUSE		= 6,
	SI_SNES			= 7,
	SI_SNES_MOUSE	= 8,

	SI_COUNT = SI_SNES_MOUSE
}ESI;

    typedef enum ESIFC
    {
        SIFC_UNSET		= -1,
        SIFC_NONE		= 0,
        SIFC_ARKANOID	= 1,
        SIFC_SHADOW		= 2,
        SIFC_4PLAYER	= 3,
        SIFC_FKB		= 4,
        SIFC_SUBORKB	= 5,
        SIFC_PEC586KB	= 6,
        SIFC_HYPERSHOT	= 7,
        SIFC_MAHJONG	= 8,
        SIFC_QUIZKING	= 9,
        SIFC_FTRAINERA	= 10,
        SIFC_FTRAINERB	= 11,
        SIFC_OEKAKIDS	= 12,
        SIFC_BWORLD		= 13,
        SIFC_TOPRIDER	= 14,
    
        SIFC_COUNT = SIFC_TOPRIDER
    }ESIFC;

    typedef struct FCEUGI FCEUGI;
    typedef struct EMUFILE_FILE EMUFILE_FILE;
    typedef struct FCEUFILE FCEUFILE;

    typedef struct FILE FILE;
    typedef uint8_t uint8;
    typedef uint16_t uint16;
    typedef uint32_t uint32;
    typedef uint64_t uint64;

    typedef int8_t int8;
    typedef int16_t int16;
    typedef int32_t int32;
    typedef int64_t int64;
    FILE* CFCEUI_UTF8fopen_C(const char* a1,const char* a2);
    bool CFCEU_IsValidUI(EFCEUI a1);
    void CFCEUD_VideoChanged();
    bool CFCEUD_PauseAfterPlayback();
    void CFCEUD_UpdatePPUView(int a1,int a2);
    void CFCEUD_UpdateNTView(int a1,bool a2);
    void CFCEUD_TraceInstruction(uint8* a1,int a2);
    void CFCEUD_DebugBreakpoint(int a1);
    void CFCEUD_CmdOpen();
    void CFCEUD_HideMenuToggle();
    void CFCEUD_ToggleStatusIcon();
    int CFCEUD_ShowStatusIcon();
    void CFCEUD_TurboToggle();
    void CFCEUD_TurboOff();
    void CFCEUD_TurboOn();
    void CFCEUD_SetEmulationSpeed(int a1);
    void CFCEUI_HandleEmuCommands(struct func* a1);
    void CFCEUD_AviStop();
    void CFCEUD_AviRecordTo();
    void CFCEUI_SetAviDisableMovieMessages(bool a1);
    bool CFCEUI_AviDisableMovieMessages();
    void CFCEUI_SetAviEnableHUDrecording(bool a1);
    bool CFCEUI_AviEnableHUDrecording();
    bool CFCEUI_AviIsRecording();
    void CFCEUI_AviSoundUpdate(void* a1,int a2);
    void CFCEUI_AviVideoUpdate(const unsigned char* a1);
    void CFCEUI_AviEnd();
    int CFCEUI_AviBegin(const char* a1);
    void CFCEUI_FrameAdvanceEnd();
    void CFCEUI_FrameAdvance();
    void CFCEUD_OnCloseGame();
    bool CFCEUD_ShouldDrawInputAids();
    void CFCEUI_ToggleEmulationPause();
    void CFCEUI_SetEmulationPaused(int a1);
    void CFCEUI_ClearEmulationFrameStepped();
    int CFCEUI_EmulationFrameStepped();
    int CFCEUI_EmulationPaused();
    int CFCEUI_DatachSet(const uint8* a1);
    void CFCEUI_FDSSelect();
    void CFCEUI_FDSInsert();
    void CFCEUI_VSUniCoin();
    void CFCEUI_VSUniSetDIP(int a1,int a2);
    uint8 CFCEUI_VSUniGetDIPs();
    void CFCEUI_VSUniToggleDIP(int a1);
    void CFCEUI_VSUniToggleDIPView();
    int CFCEUI_NSFGetInfo(uint8* a1,uint8* a2,uint8* a3,int a4);
    int CFCEUI_NSFChange(int a1);
    void CFCEUI_NSFSetVis(int a1);
    void CFCEUI_SetLowPass(int a1);
    uint32 CFCEUI_CRC32(uint32 a1,uint8* a2,uint32 a3);
    void CFCEUI_GetIVectors(uint16* a1,uint16* a2,uint16* a3);
    uint16 CFCEUI_Disassemble(void* a1,uint16 a2,char* a3);
    void CFCEUI_IRQ();
    void CFCEUI_NMI();
    void CFCEUI_MemPoke(uint16 a1,uint8 a2,int a3);
    uint8 CFCEUI_MemSafePeek(uint16 a1);
    void CFCEUI_MemDump(uint16 a1,int32 a2,struct func* a3);
    void CFCEUI_SetDirOverride(int a1,char* a2);
    void CFCEUI_CheatSearchSetCurrentAsOriginal();
    void CFCEUI_CheatSearchShowExcluded();
    int CFCEUI_SetCheat(uint32 a1,const char* a2,int32 a3,int32 a4,int a5,int a6,int a7);
    int CFCEUI_GetCheat(uint32 a1,char** a2,uint32* a3,uint8* a4,int* a5,int* a6,int* a7);
    void CFCEUI_ListCheats(struct func* a1,void* a2);
    void CFCEUI_CheatSearchEnd(int a1,uint8 a2,uint8 a3);
    void CFCEUI_CheatSearchBegin();
    void CFCEUI_CheatSearchGet(struct func* a1,void* a2);
    void CFCEUI_CheatSearchGetRange(uint32 a1,uint32 a2,struct func* a3);
    int32 CFCEUI_CheatSearchGetCount();
    int CFCEUI_ToggleCheat(uint32 a1);
    int CFCEUI_DelCheat(uint32 a1);
    int CFCEUI_AddCheat(const char* a1,uint32 a2,uint8 a3,int a4,int a5);
    int CFCEUI_DecodeGG(const char* a1,int* a2,int* a3,int* a4);
    int CFCEUI_DecodePAR(const char* a1,int* a2,int* a3,int* a4,int* a5);
    void CFCEUI_SaveSnapshotAs();
    void CFCEUI_SaveSnapshot();
    int32 CFCEUI_GetDesiredFPS();
    void CFCEUD_LuaRunFrom();
    void CFCEUD_MovieReplayFrom();
    void CFCEUD_MovieRecordTo();
    void CFCEUD_SetInput(bool a1,bool a2,ESI a3,ESI a4,ESIFC a5);
    void CFCEUD_LoadStateFrom();
    void CFCEUD_SaveStateAs();
    void CFCEUI_LoadState(const char* a1,bool a2);
    void CFCEUI_SaveState(const char* a1,bool a2);
    void CFCEUI_SelectStateNext(int a1);
    int CFCEUI_SelectState(int a1,int a2);
    void CFCEUD_SoundVolumeAdjust(int a1);
    void CFCEUD_SoundToggle();
    void CFCEUI_SetSoundQuality(int a1);
    void CFCEUI_SetPCMVolume(uint32 a1);
    void CFCEUI_SetNoiseVolume(uint32 a1);
    void CFCEUI_SetSquare2Volume(uint32 a1);
    void CFCEUI_SetSquare1Volume(uint32 a1);
    void CFCEUI_SetTriangleVolume(uint32 a1);
    void CFCEUI_SetSoundVolume(uint32 a1);
    void CFCEUI_Sound(int a1);
    void CFCEUI_SetUserPalette(uint8* a1,int a2);
    void CFCEUI_SetRenderedLines(int a1,int a2,int a3,int a4);
    int CFCEUI_GetCurrentVidSystem(int* a1,int* a2);
    void CFCEUI_SetRegion(int a1);
    void CFCEUI_SetVidSystem(int a1);
    void CFCEUI_SetGameGenie(bool a1);
    void CFCEUI_Kill();
    void CFCEUI_CloseGame();
    void CFCEUI_Emulate(uint8** a1,int32** a2,int32* a3,int a4);
    bool CFCEUI_Initialize();
    FCEUGI* CFCEUI_LoadGameVirtual(const char* a1,int a2,bool a3);
    FCEUGI* CFCEUI_LoadGame(const char* a1,int a2,bool a3);
    void CFCEUI_GetRenderPlanes(bool& a1,bool& a2);
    void CFCEUI_SetRenderPlanes(bool a1,bool a2);
    void CFCEUI_DisableSpriteLimitation(int a1);
    void CFCEUI_UseInputPreset(int a1);
    bool CFCEUI_GetInputMicrophone();
    bool CFCEUI_GetInputFourscore();
    void CFCEUI_SetInputFourscore(bool a1);
    void CFCEUI_SetInputFC(ESIFC a1,void* a2,int a3);
    void CFCEUI_SetInput(int a1,ESI a2,void* a3,int a4);
    void CFCEUI_SetNTSCTH(bool a1,int a2,int a3);
    void CFCEUI_GetNTSCTH(int* a1,int* a2);
    void CFCEUI_NTSCINC();
    void CFCEUI_NTSCDEC();
    void CFCEUI_NTSCSELTINT();
    void CFCEUI_NTSCSELHUE();
    void CFCEUI_PowerNES();
    void CFCEUI_ResetNES();
    int CFCEUI_EndWaveRecord();
    bool CFCEUI_BeginWaveRecord(const char* a1);
    void CFCEUD_NetworkClose();
    void CFCEUI_NetplayText(uint8* a1);
    void CFCEUD_NetplayText(uint8* a1);
    int CFCEUD_RecvData(void* a1,uint32 a2);
    int CFCEUD_SendData(void* a1,uint32 a2);
    void CFCEUI_NetplayStop();
    int CFCEUI_NetplayStart(int a1,int a2);
    void CFCEUD_Message(const char* a1);
    void CFCEUD_PrintError(const char* a1);
    void CFCEUD_GetPalette(uint8 a1,uint8* a2,uint8* a3,uint8* a4);
    void CFCEUD_SetPalette(uint8 a1,uint8 a2,uint8 a3,uint8 a4);
    const char* CFCEUD_GetCompilerString();
    //ArchiveScanRecord CFCEUD_ScanArchive(std::__cxx11::string a1);
    //FCEUFILE* CFCEUD_OpenArchive(ArchiveScanRecord& a1,std::__cxx11::string& a2,std::__cxx11::string* a3);
    //FCEUFILE* CFCEUD_OpenArchiveIndex(ArchiveScanRecord& a1,std::__cxx11::string& a2,int a3);
    EMUFILE_FILE* CFCEUD_UTF8_fstream(const char* a1,const char* a2);
    FILE* CFCEUD_UTF8fopen(const char* a1,const char* a2);
    //void CFCEU_SplitArchiveFilename(std::__cxx11::string a1,std::__cxx11::string& a2,std::__cxx11::string& a3,std::__cxx11::string& a4);
    //std::__cxx11::string CFCEU_MakeFName(int a1,int a2,const char* a3);
    //std::__cxx11::string CFCEU_MakePath(int a1,const char* a2);
    //std::__cxx11::string CFCEU_GetPath(int a1);
    int CFCEU_fisarchive(FCEUFILE* a1);
    uint64 CFCEU_fgetsize(FCEUFILE* a1);
    int CFCEU_fgetc(FCEUFILE* a1);
    int CFCEU_read16le(uint16* a1,FCEUFILE* a2);
    int CFCEU_read32le(uint32* a1,FCEUFILE* a2);
    uint64 CFCEU_ftell(FCEUFILE* a1);
    int CFCEU_fseek(FCEUFILE* a1,long int a2,int a3);
    uint64 CFCEU_fwrite(void* a1,size_t a2,size_t a3,FCEUFILE* a4);
    uint64 CFCEU_fread(void* a1,size_t a2,size_t a3,FCEUFILE* a4);
    int CFCEU_fclose(FCEUFILE* a1);
    bool CFCEU_isFileInArchive(const char* a1);
    FCEUFILE* CFCEU_fopen(const char* a1,const char* a2,const char* a3,char* a4,int a5,const char** a6);
    void CFCEU_PutImage();
    void CFCEU_TogglePPU();
    void CFCEU_DispMessageOnMovie(char* a1);
    void CFCEU_DispMessage(char* a1,int a2);
    void CFCEU_printf(char* a1);
    void CFCEU_PrintError(char* a1);
    int CFCEU_TextScanlineOffsetFromBottom(int a1);
    int CFCEU_TextScanlineOffset(int a1);
    void CFCEU_WriteRomByte(uint32 a1,uint8 a2);
    uint8 CFCEU_ReadRomByte(uint32 a1);
    char* CFCEUI_GetAboutString();
    void CFCEUI_RewindToLastAutosave();
    void CFCEU_ResetVidSys();
    void CFCEU_MemoryRand(uint8* a1,uint32 a2);
    uint16 CFCEU_de16lsb(uint8* a1);
    uint32 CFCEU_de32lsb(uint8* a1);
    uint64 CFCEU_de64lsb(uint8* a1);
    void CFCEU_en16lsb(uint8* a1,uint16 a2);
    void CFCEU_en32lsb(uint8* a1,uint32 a2);
    int CFCEUD_FastDrawPix32(uint32*pix32,uint8_t*pix);
]]

--local fceux = ffi.load('fceux');
fceux = loadso('fceux');

fc = {};
function fc:Init()
    --初始化fceux
    assert(fceux.CFCEUI_Initialize()~=0);
end

function fc:LoadROM(fn)
    --FCEU_fopen(const char *path, const char *ipsfn, char *mode, char *ext, int index=-1, const char** extensions = 0);
    local self = {
        file = fceux.CFCEU_fopen(fn,nil,'rb',nil,-1,nil),
    }
    --初始化驱动
    --初始化声音
    fceux.CFCEUI_SetSoundVolume(150); --音量,0到100

    fceux.CFCEUI_SetSoundQuality(1);
    fceux.CFCEUI_Sound(44100);
    fceux.CFCEUI_SetTriangleVolume(256);  --音源三角波
    fceux.CFCEUI_SetSquare1Volume(256);   --音源方波1
    fceux.CFCEUI_SetSquare2Volume(256);   --音源方波2
    fceux.CFCEUI_SetNoiseVolume(256);     --噪音
    fceux.CFCEUI_SetPCMVolume(256);       --pcm


    fceux.CFCEUI_LoadGameVirtual(fn,1,false);


    --初始化输入
    --void CFCEUI_SetInput(int a1,ESI a2,void* a3,int a4);
    --void CFCEUI_SetInputFC(ESIFC a1,void* a2,int a3);
    --void FCEUI_SetInputFourscore(bool attachFourscore)
    local attrib = 0;
    local InputDPtr = ffi.new('uint8_t[0x100]');
    self.InputDPtr = InputDPtr;
    for port=0,1 do
        local att = 0;
        local type = fceux.SI_GAMEPAD;
        fceux.CFCEUI_SetInput(port,type,InputDPtr,att);
    end
	fceux.CFCEUI_SetInputFC (fceux.SIFC_NONE, InputDPtr, attrib);
	fceux.CFCEUI_SetInputFourscore (true);

    local pixptr = ffi.new('uint8_t*[2]');
    local soundptr = ffi.new('int32*[2]');
    local ssizeptr = ffi.new('int[2]');
    local drawPix = ffi.new('uint32_t[256*256]');
    local fskipc = 0;
    local u16_data = ffi.new('uint16_t[1024*512]');
    function self:Close()
        fceux.CFCEU_fclose(self.file);
    end
    local sound = CreateSound(nil,{
        channels = 1, --只有一个声道
    });
    function self:SetKey(key)
        InputDPtr[0] = key;
    end
    function self:Emulate()
        fceux.CFCEUI_Emulate(pixptr,soundptr,ssizeptr,fskipc);
        local pix = pixptr[0];
        -- local ptr = drawPix;
        -- --可能需要设置透明色
        -- for y=0,240 do
        --     for x=0,255 do
        --         local b = ffi.cast('uint8_t*',ptr);
        --         local g = b + 1;
        --         local r = b + 2;
        --         local p = pix[x+y*256];
        --         if(p~=0)then 
        --             r[3] = 0xff;
        --             fceux.CFCEUD_GetPalette(p,r,g,b);
        --         else
        --             r[3] = 0;
        --         end
        --         ptr = ptr + 1;
        --     end
        -- end
        fceux.CFCEUD_FastDrawPix32(drawPix,pix);
        local wave_data = soundptr[0];
        local len = ssizeptr[0];
        print(wave_data,len);
        for i=0,len do
            u16_data[i] = wave_data[i];
        end
        sound:WriteWave(u16_data,len*2);
    end
    function self:GetPix()
        return drawPix,256,224;
    end
    return self;
end
